#ifndef RAII_H
#define RAII_H
#include <chrono>

template<typename F>
struct _FINAL_ACTION
{
  _FINAL_ACTION(F f) : clean(f), isDiscard(false) {}
  ~_FINAL_ACTION() { if (!isDiscard) clean(); }
  void discard() { isDiscard = true; }

  F clean;
  bool isDiscard;
};

template<typename F>
_FINAL_ACTION<F> finally(F f)
{
  return _FINAL_ACTION<F>(f);
}

#define ExitCaller(Name, Caller) auto epilog_##Name = finally(Caller)
#define DelExitCaller(Name) epilog_##Name.discard()


/**
* [From UE4]
* utility template for a class that should not be copyable.
* Derive from this class to make your class non-copyable
*/
class FNoncopyable
{
protected:
	// ensure the class cannot be constructed directly
	FNoncopyable () {}
	// the class should not be used polymorphically
	~FNoncopyable () {}
private:
	FNoncopyable (const FNoncopyable&);
	FNoncopyable& operator=(const FNoncopyable&);
};

/**
* [From UE4]
* exception-safe guard around saving/restoring a value.
* Commonly used to make sure a value is restored
* even if the code early outs in the future.
* Usage:
*  	TGuardValue<bool> GuardSomeBool(bSomeBool, false); // Sets bSomeBool to false, and restores it in dtor.
*/
template <typename Type>
struct TGuardValue : private FNoncopyable
{
	TGuardValue (Type& ReferenceValue, const Type& NewValue)
		: RefValue (ReferenceValue), OldValue (ReferenceValue)
	{
		RefValue = NewValue;
	}
	~TGuardValue ()
	{
		RefValue = OldValue;
	}

	/**
	* Overloaded dereference operator.
	* Provides read-only access to the original value of the data being tracked by this struct
	*
	* @return	a const reference to the original data value
	*/
	inline const Type& operator*() const
	{
		return OldValue;
	}

private:
	Type& RefValue;
	Type OldValue;
};

struct TimeSize
{
	TimeSize () : m_timepoint (std::chrono::steady_clock::now ()) {}

	inline float s (bool Reset = true)
	{
		return ms (Reset) / 1000.0f;
	}

	inline long long ms (bool Reset = true)
	{
		std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now ();
		long long ret = std::chrono::duration_cast<std::chrono::milliseconds>(end - m_timepoint).count ();
		if (Reset)
		{
			m_timepoint = std::chrono::steady_clock::now ();
		}
		return ret;
	}

	std::chrono::steady_clock::time_point m_timepoint;
};

#endif // RAII_H
